package awt.robot;

import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

import javax.swing.SwingWorker;
import javax.swing.Timer;

import concurrency.Condition;

import sun.awt.AppContext;
import sun.swing.AccumulativeRunnable;
import util.ReflectUtils;

/**
 * Application is idle when there is no active SwingWorker at the moment.
 * 
 * @author Mykhaylo Adamovych
 */
public class SwingIdlePolicy extends AbstractExecutorPolicy {

    /**
     * Usually too costly and unnecessary to use as default idle policy. Use only with components that use Timers only where needed.
     */
    public static boolean isThereSheduledTimers() {
        try {
            final Class<?> timerQueueClass = Class.forName("javax.swing.TimerQueue");
            final Object timerQueue = ReflectUtils.invoke(timerQueueClass, "sharedInstance");
            Timer timer = ReflectUtils.get(timerQueue, "firstTimer");
            while (timer != null) {
                if (!timer.isRepeats())
                    return true;
                timer = ReflectUtils.get(timer, "nextTimer");
            }
            return false;
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void waitForSheduledTimers() {
        Sync.waitUntil(new Condition() {
            @Override
            public boolean isSatisfied() {
                return !isThereSheduledTimers();
            }
        });
    }
    
    @Override
    public boolean isIdle() {
        return isAccumulativeRunnableEmpty() && super.isIdle();
    }
    
    @Override
    protected boolean isThereActiveExecutors() {
        final AppContext appContext = AppContext.getAppContext();
        final ThreadPoolExecutor executorService = (ThreadPoolExecutor) appContext.get(SwingWorker.class);
        return executorService != null && (executorService.getActiveCount() > 0 || !executorService.getQueue().isEmpty());
    }
    
    private boolean isAccumulativeRunnableEmpty() {
        final AccumulativeRunnable<Runnable> accumulativeRunnable = ReflectUtils.invoke(SwingWorker.class, "getDoSubmit");
        final List<Runnable> arguments = ReflectUtils.get(accumulativeRunnable, "arguments");
        return arguments == null;
    }
}
